Public Domain.https://finance.yahoo.com/quote/JPM/history?p=JPM.
Esta análise de dados tem como único propósito a demonstração e teste da implementação básica de uma rede neural LSTM. Não deve ser interpretada como um conselho de investimento no mercado de ações, e não tem a intenção de prever valores de ações com alta precisão.
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Dropout, SimpleRNN
from tensorflow.keras.optimizers import SGD
import datetime as dt
from sklearn.preprocessing import MinMaxScaler
import warnings
warnings.filterwarnings("ignore")
df1=pd.read_csv('JPM.csv')
df1.head()
| Date | Open | High | Low | Close | Adj Close | Volume | |
|---|---|---|---|---|---|---|---|
| 0 | 2013-07-22 | 56.169998 | 56.669998 | 55.950001 | 56.560001 | 42.783409 | 15999700 |
| 1 | 2013-07-23 | 56.720001 | 56.889999 | 56.470001 | 56.669998 | 42.866615 | 10577100 |
| 2 | 2013-07-24 | 56.919998 | 56.930000 | 56.380001 | 56.630001 | 42.836361 | 16665000 |
| 3 | 2013-07-25 | 56.450001 | 56.500000 | 55.889999 | 56.500000 | 42.738018 | 13870200 |
| 4 | 2013-07-26 | 56.060001 | 56.220001 | 55.599998 | 56.049999 | 42.397629 | 16762300 |
df1.isnull().sum()
Date 0 Open 0 High 0 Low 0 Close 0 Adj Close 0 Volume 0 dtype: int64
prices = df1['Close']
fig = go.Figure(data=[go.Candlestick(x=df1['Date'],
open=df1['Open'],
high=df1['High'],
low=df1['Low'],
close=df1['Close'])])
fig.show()
## Visualizando valores de fechamento
plt.figure(figsize=(12,6))
plt.plot(df1['Close'])
plt.xticks(range(0,df1.shape[0],200), df1['Date'].loc[::200], rotation=45)
plt.xlabel('Datas', fontsize=15)
plt.ylabel('Preço Fechamento', fontsize=15)
plt.title('Histórico de Preço JP Morgan', fontsize=17)
plt.show()
#É interessante manter os dias anteriores (neste caso utilizei os 20 dias anteriores ao valor previsto), uma vez que o modelo
# tenta prever valores próximos.
time_step=20
#Utilizei 95% dos valores para treino do modelo, o resto para teste (representação gráfica abaixo).
train_size = int(len(prices) * 0.95)
test_size = len(prices) - train_size
#Definindo os dados de teste e treino de acordo com o tamanho acima.
# Além disso, transformei-os no tipo array do numpy para facilitar o uso da série temporal com a biblioteca TensorFlow.
train_data, test_data = np.array(prices[0:train_size]), np.array(prices[train_size - time_step:])
check_data = np.array(prices[train_size:])
test_data.shape
(146,)
## Visualizando o intervalo de treinamento e o intervalo de teste:
train_xPlot= np.arange(0, len(train_data))
test_xPlot = np.arange(len(train_data),len(train_data)+len(test_data))
plt.figure(figsize=(12,6))
plt.plot(train_xPlot, train_data, color='blue', label='Train Data')
plt.plot(test_xPlot, test_data, color='green', label='Test Data')
plt.xticks(range(0,df1.shape[0],200), df1['Date'].loc[::200], rotation=45)
plt.xlabel('Datas')
plt.ylabel('Preço Medio')
plt.legend()
plt.show()
Normalizei os dados para evitar que alguns atributos com valores numericamente maiores dominem o processo de treinamento em detrimento de outros, que possuem valores numericamente menores. Assim, melhora-se o modelo como um todo.
#O método utilizado, neste caso, é o MinMaxScaler.
#Este método transforma os dados em uma escala entre um intervalo definido, que neste caso é entre 0 e 1.
scaler = MinMaxScaler(feature_range=(0,1))
#Utilizei o 'fit_transform' para ajustar o scale com o conjunto de treino.
train_data_norm = scaler.fit_transform(np.array(train_data).reshape(-1,1))
#Dessa forma, o de teste e o de checagem não precisam fazer o 'fit', apenas receber as transformações necessárias
test_data_norm=scaler.transform(np.array(test_data).reshape(-1,1))
check_data_norm = scaler.transform(np.array(check_data).reshape(-1,1))
Nesta etapa, feita a preparação ideal dos conjuntos de dados para treinamento, teste e validação da rede neural.
#Treino
X_train, y_train = [], []
for i in range(time_step, len(train_data)):
X_train.append(train_data_norm[i-time_step:i])
y_train.append(train_data_norm[i])
#Teste
X_test=[]
for i in range(time_step, time_step + len(check_data)):
X_test.append(test_data_norm[i-time_step:i])
#Validação
X_check, y_check = [], []
for i in range(time_step, len(check_data_norm)):
X_check.append(check_data_norm[i-time_step:i])
y_check.append(check_data_norm[i])
#matriz numpy
X_train = np.array(X_train)
y_train= np.array(y_train)
X_test = np.array(X_test)
X_check = np.array(X_check)
y_check = np.array(y_check)
X_test.shape
(126, 20, 1)
#Rede neural profunda sequencial
model = Sequential()
model.add(LSTM(100, return_sequences=False, input_shape=(time_step,1)))
## semelhante ao sigmoid do LSTM (exercido aqui por Dense)
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm (LSTM) (None, 100) 40800
dense (Dense) (None, 1) 101
=================================================================
Total params: 40,901
Trainable params: 40,901
Non-trainable params: 0
_________________________________________________________________
# Treinando o modelo
h = model.fit(X_train, y_train, validation_data=(X_check, y_check), epochs=30, batch_size=32)
Epoch 1/30 75/75 [==============================] - 3s 15ms/step - loss: 0.0080 - val_loss: 0.0011 Epoch 2/30 75/75 [==============================] - 1s 10ms/step - loss: 7.7747e-04 - val_loss: 0.0012 Epoch 3/30 75/75 [==============================] - 1s 10ms/step - loss: 7.4997e-04 - val_loss: 9.3597e-04 Epoch 4/30 75/75 [==============================] - 1s 10ms/step - loss: 6.8410e-04 - val_loss: 9.2730e-04 Epoch 5/30 75/75 [==============================] - 1s 10ms/step - loss: 6.5210e-04 - val_loss: 9.9417e-04 Epoch 6/30 75/75 [==============================] - 1s 10ms/step - loss: 6.4084e-04 - val_loss: 0.0020 Epoch 7/30 75/75 [==============================] - 1s 10ms/step - loss: 6.0964e-04 - val_loss: 8.4362e-04 Epoch 8/30 75/75 [==============================] - 1s 11ms/step - loss: 5.4071e-04 - val_loss: 7.5074e-04 Epoch 9/30 75/75 [==============================] - 1s 10ms/step - loss: 5.4867e-04 - val_loss: 7.4575e-04 Epoch 10/30 75/75 [==============================] - 1s 11ms/step - loss: 4.9040e-04 - val_loss: 6.8225e-04 Epoch 11/30 75/75 [==============================] - 1s 11ms/step - loss: 5.0460e-04 - val_loss: 7.0960e-04 Epoch 12/30 75/75 [==============================] - 1s 10ms/step - loss: 4.7567e-04 - val_loss: 6.5350e-04 Epoch 13/30 75/75 [==============================] - 1s 10ms/step - loss: 4.3930e-04 - val_loss: 6.4182e-04 Epoch 14/30 75/75 [==============================] - 1s 10ms/step - loss: 4.1386e-04 - val_loss: 6.7141e-04 Epoch 15/30 75/75 [==============================] - 1s 11ms/step - loss: 4.0710e-04 - val_loss: 5.9287e-04 Epoch 16/30 75/75 [==============================] - 1s 11ms/step - loss: 4.1017e-04 - val_loss: 6.5027e-04 Epoch 17/30 75/75 [==============================] - 1s 10ms/step - loss: 3.7765e-04 - val_loss: 5.7316e-04 Epoch 18/30 75/75 [==============================] - 1s 10ms/step - loss: 3.8240e-04 - val_loss: 0.0010 Epoch 19/30 75/75 [==============================] - 1s 10ms/step - loss: 3.6395e-04 - val_loss: 5.4218e-04 Epoch 20/30 75/75 [==============================] - 1s 11ms/step - loss: 3.5333e-04 - val_loss: 5.7757e-04 Epoch 21/30 75/75 [==============================] - 1s 11ms/step - loss: 3.5803e-04 - val_loss: 5.2111e-04 Epoch 22/30 75/75 [==============================] - 1s 10ms/step - loss: 3.6107e-04 - val_loss: 7.7913e-04 Epoch 23/30 75/75 [==============================] - 1s 11ms/step - loss: 3.4948e-04 - val_loss: 5.8040e-04 Epoch 24/30 75/75 [==============================] - 1s 11ms/step - loss: 3.5082e-04 - val_loss: 4.9181e-04 Epoch 25/30 75/75 [==============================] - 1s 11ms/step - loss: 3.3290e-04 - val_loss: 5.0026e-04 Epoch 26/30 75/75 [==============================] - 1s 10ms/step - loss: 3.3279e-04 - val_loss: 4.7841e-04 Epoch 27/30 75/75 [==============================] - 1s 10ms/step - loss: 3.1864e-04 - val_loss: 6.2307e-04 Epoch 28/30 75/75 [==============================] - 1s 10ms/step - loss: 3.1749e-04 - val_loss: 5.0682e-04 Epoch 29/30 75/75 [==============================] - 1s 10ms/step - loss: 3.0593e-04 - val_loss: 4.5198e-04 Epoch 30/30 75/75 [==============================] - 1s 10ms/step - loss: 3.2663e-04 - val_loss: 4.3569e-04
plt.plot(h.history["loss"],label='loss')
plt.plot(h.history["val_loss"],label='val_loss')
plt.legend()
plt.show()
#predição de valores pela rede
predict = model.predict(X_test)
#transformação inversa do normalizador para utilizar os valores no gráfico
predict = scaler.inverse_transform(predict)
real = test_data
4/4 [==============================] - 0s 4ms/step
plt.figure(figsize=(12,6))
plt.plot(real,color='green',label='real')
plt.plot(predict, color='red',label='previsão')
plt.xticks(range(0,len(real), 20), df1['Date'].iloc[-len(real)::20],rotation=45)
plt.xlabel('Datas')
plt.ylabel('Preço Médio')
plt.title('Projeção Preços JP Morgan')
plt.legend()
plt.show()
Podemos observar que, mesmo quando um modelo é bem treinado e apresenta baixos valores de perda (loss) e perda de validação (val_loss), as previsões podem não ser suficientemente precisas para lidar com os preços das ações exclusivamente com base no algoritmo.